package com.luqili.utils.pub.crypto;

import com.luqili.utils.pub.consver.ByteConverUtils;
import org.apache.commons.codec.binary.Hex;

import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Random;

/**
 * 加密机
 *
 * @author luqili 2017年7月13日 下午11:52:36
 */
public class Encryptors {
    private static final String CHARSET_NAME = "UTF-8";

    /**
     * AES算法解密
     * <li>加密结果以Base64编码
     * <li>pwd为16位字符串
     *
     * @param src
     * @param pwd
     * @return
     */
    public static String aesDecrypt(String src, String pwd) {
        try {
            byte[] desKey = pwd.getBytes(CHARSET_NAME);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKeySpec keySpec = new SecretKeySpec(desKey, "AES");
            IvParameterSpec iv = new IvParameterSpec(desKey, 0, 16);
            cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
            byte[] input = Base64.getDecoder().decode(src);
            byte[] result = cipher.doFinal(input);
            return new String(result, CHARSET_NAME);
        } catch (Exception e) {
            throw new RuntimeException("AES decrypt is error !", e);
        }
    }

    /**
     * 对字符串进行加密
     *
     * @param src
     * @param pwd
     * @return
     */
    public static String aesEncrypt(String src, String pwd) {
        try {
            byte[] desKey = pwd.getBytes(CHARSET_NAME);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKeySpec keySpec = new SecretKeySpec(desKey, "AES");
            IvParameterSpec iv = new IvParameterSpec(desKey, 0, 16);
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
            byte[] input = src.getBytes(CHARSET_NAME);
            byte[] result = cipher.doFinal(input);
            return Base64.getEncoder().encodeToString(result);
        } catch (Exception e) {
            throw new RuntimeException("AES encrypt is error !", e);
        }
    }

    /**
     * 新建密钥对
     *
     * @return KeyPair对象
     */
    public static KeyPair createRsaKeyPair() {
        KeyPair myPair;
        long mySeed;
        mySeed = System.currentTimeMillis();
        try {
            KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
            SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");
            random.setSeed(mySeed);
            keyGen.initialize(1024, random);
            myPair = keyGen.generateKeyPair();
        } catch (Exception e1) {
            return null;
        }
        return myPair;
    }

    /**
     * 建立新的密钥对，返回打包的byte[]形式私钥和公钥
     *
     * @return 包含打包成byte[]形式的私钥和公钥的object[], 其中，object[0]为私钥byte[],object[1]为公钥byte[]
     */
    public static byte[][] createRSAKeyPairInByte() {
        KeyPair newKeyPair = createRsaKeyPair();
        if (newKeyPair == null) {
            return null;
        }
        byte[][] re = new byte[2][];
        PrivateKey priv = newKeyPair.getPrivate();
        byte[] b_priv = priv.getEncoded();
        PublicKey pub = newKeyPair.getPublic();
        byte[] b_pub = pub.getEncoded();
        re[0] = b_priv;
        re[1] = b_pub;
        return re;
    }

    /**
     * 用RSA私钥解密
     *
     * @param privKeyInByte 私钥打包成byte[]形式
     * @param data          要解密的数据
     * @return 解密数据
     */
    public static byte[] decryptByRSAByPrivateKey(byte[] privKeyInByte, byte[] data) {
        try {
            PKCS8EncodedKeySpec priv_spec = new PKCS8EncodedKeySpec(privKeyInByte);
            KeyFactory mykeyFactory = KeyFactory.getInstance("RSA");
            PrivateKey privKey = mykeyFactory.generatePrivate(priv_spec);
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.DECRYPT_MODE, privKey);
            return cipher.doFinal(data);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 用RSA公钥解密
     *
     * @param privateKey 公钥打包成byte[]形式
     * @param data       要解密的数据
     * @return 解密数据
     */

    public static byte[] decryptByRSAByPubKey(byte[] pubKeyInByte, byte[] data) {
        try {
            KeyFactory mykeyFactory = KeyFactory.getInstance("RSA");
            X509EncodedKeySpec pub_spec = new X509EncodedKeySpec(pubKeyInByte);
            PublicKey pubKey = mykeyFactory.generatePublic(pub_spec);
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            // Cipher cipher = Cipher.getInstance(mykeyFactory.getAlgorithm());
            cipher.init(Cipher.DECRYPT_MODE, pubKey);
            return cipher.doFinal(data);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * RSA解密
     *
     * @param publicKey
     * @param data
     * @return
     */
    public static byte[] decryptByRSAByPubKey(String publicKey, byte[] data) {
        byte[] key = Base64.getDecoder().decode(publicKey);
        return decryptByRSAByPubKey(key, data);
    }

    /**
     * RSA解密
     *
     * @param privateKey
     * @param data
     * @return
     */
    public static byte[] decryptByRSAByPubKey(String publicKey, String dataStr) {
        byte[] data = Base64.getDecoder().decode(dataStr);
        return decryptByRSAByPubKey(publicKey, data);
    }

    /**
     * 分块解密
     *
     * @param pubKeyInByte
     * @param rsaData
     * @return
     */
    public static byte[] decryptByRSAByPubKeyLongData(byte[] pubKeyInByte, byte[] rsaData) {
        byte[] srcData = new byte[0];
        for (int i = 0; i < rsaData.length; ) {
            int start = i;
            i += 128;
            if (i > rsaData.length) {
                i = rsaData.length;
            }
            byte[] d1 = Arrays.copyOfRange(rsaData, start, i);
            byte[] e1 = Encryptors.decryptByRSAByPubKey(pubKeyInByte, d1);
            srcData = ByteConverUtils.concat(srcData, e1);
        }
        return srcData;
    }

    /**
     * 使用RSA私钥加密数据
     *
     * @param pubKeyInByte 打包的byte[]形式私钥
     * @param data         要加密的数据
     * @return 加密数据 不超过117位
     */
    public static byte[] encryptByRSAByPrivateKey(byte[] privKeyInByte, byte[] data) {
        try {
            PKCS8EncodedKeySpec priv_spec = new PKCS8EncodedKeySpec(privKeyInByte);
            KeyFactory mykeyFactory = KeyFactory.getInstance("RSA");
            PrivateKey privKey = mykeyFactory.generatePrivate(priv_spec);
            // Cipher cipher = Cipher.getInstance(mykeyFactory.getAlgorithm());
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, privKey);
            return cipher.doFinal(data);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * RSA加密
     *
     * @param privateKey
     * @param data
     * @return
     */
    public static byte[] encryptByRSAByPrivateKey(String privateKey, byte[] data) {
        byte[] key = Base64.getDecoder().decode(privateKey);
        return encryptByRSAByPrivateKey(key, data);
    }

    /**
     * 分块加密
     *
     * @param privKeyInByte
     * @param data
     * @return
     */
    public static byte[] encryptByRSAByPrivateKeyLongData(byte[] privKeyInByte, byte[] data) {
        byte[] rsaData = new byte[0];
        int len = data.length;
        for (int i = 0; i < len; ) {
            int start = i;
            i += 117;
            if (i > len) {
                i = len;
            }
            byte[] d1 = Arrays.copyOfRange(data, start, i);
            byte[] e1 = encryptByRSAByPrivateKey(privKeyInByte, d1);
            rsaData = ByteConverUtils.concat(rsaData, e1);
        }
        return rsaData;
    }

    /**
     * RSA加密
     *
     * @param privateKey
     * @param data       最大117位
     * @return
     */
    public static String encryptByRSAByPrivateKeyToString(String privateKey, byte[] data) {
        byte[] e1 = encryptByRSAByPrivateKey(privateKey, data);
        return Base64.getEncoder().encodeToString(e1);
    }

    /**
     * 使用RSA公钥加密数据
     *
     * @param pubKeyInByte 打包的byte[]形式公钥
     * @param data         要加密的数据
     * @return 加密数据
     */
    public static byte[] encryptByRSAByPubKey(byte[] pubKeyInByte, byte[] data) {
        try {
            KeyFactory mykeyFactory = KeyFactory.getInstance("RSA");
            X509EncodedKeySpec pub_spec = new X509EncodedKeySpec(pubKeyInByte);
            PublicKey pubKey = mykeyFactory.generatePublic(pub_spec);
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
            return cipher.doFinal(data);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 获得随机数组Base64编码
     *
     * @param byteCount 长度
     * @return
     */
    public static String getRandomKeyToBase64(int byteCount) {
        Random random = new SecureRandom();
        byte[] keys = new byte[byteCount];
        random.nextBytes(keys);
        return Base64.getEncoder().encodeToString(keys);
    }

    /**
     * 获得随机数组Hex编码
     *
     * @param byteCount 长度
     * @return
     */
    public static String getRandomKeyToHex(int byteCount) {
        Random random = new SecureRandom();
        byte[] keys = new byte[byteCount];
        random.nextBytes(keys);
        return Hex.encodeHexString(keys);
    }

    /**
     * HmacSHA1 加密
     *
     * @param src
     * @param pwd
     * @return
     */
    public static String hmacSHA1(String src, String pwd) {
        try {
            byte[] desKey = pwd.getBytes(CHARSET_NAME);
            Mac mac = Mac.getInstance("HmacSHA1");
            SecretKeySpec keySpec = new SecretKeySpec(desKey, "MAC");
            mac.init(keySpec);
            byte[] result = mac.doFinal(src.getBytes(CHARSET_NAME));
            return Base64.getEncoder().encodeToString(result);
        } catch (Exception e) {
            throw new RuntimeException("HmacSHA1 decrypt is error !", e);
        }
    }

    /**
     * HmacSHA256 加密
     *
     * @param src
     * @param pwd
     * @return
     */
    public static String hmacSHA256(String src, String pwd) {
        try {
            byte[] desKey = pwd.getBytes(CHARSET_NAME);
            Mac mac = Mac.getInstance("HmacSHA256");
            SecretKeySpec keySpec = new SecretKeySpec(desKey, "MAC");
            mac.init(keySpec);
            byte[] result = mac.doFinal(src.getBytes(CHARSET_NAME));
            return Base64.getEncoder().encodeToString(result);
        } catch (Exception e) {
            throw new RuntimeException("AES decrypt is error !", e);
        }
    }

}
